"""
工单服务
处理工单的创建、更新、查询和自动化处理
"""

from datetime import datetime
from typing import Optional, List, Dict, Any
import uuid
from sqlalchemy.orm import Session
from sqlalchemy import and_, or_, desc, func

from src.config import ticket_settings
from src.models.ticket import (
    Ticket, TicketReply, TicketCreate, TicketUpdate, TicketReplyCreate,
    TicketResponse, TicketReplyResponse, TicketStats, TicketFilter,
    TicketStatus, TicketPriority, TicketCategory
)
from src.models.user import User, UserRole, Permission, UserStatus
from src.services.notification_service import NotificationService
from src.utils.logging import get_logger
from src.services.monitoring_service import monitoring_service
from src.exceptions import DatabaseError, ServiceError, AuthorizationError
from fastapi import HTTPException, status
import asyncio
import concurrent.futures
import threading

logger = get_logger(__name__)

# 创建一个全局事件循环用于异步操作
_notification_loop = None
_notification_loop_thread = None

def _start_notification_loop():
    """启动通知事件循环"""
    global _notification_loop, _notification_loop_thread
    if _notification_loop is None:
        _notification_loop = asyncio.new_event_loop()
        _notification_loop_thread = threading.Thread(target=_notification_loop.run_forever, daemon=True)
        _notification_loop_thread.start()

def _run_async_notification(coro):
    """在专用线程中运行异步通知"""
    global _notification_loop
    if _notification_loop is None:
        _start_notification_loop()
    
    # 创建一个future来获取结果
    future = concurrent.futures.Future()
    
    async def run_and_set_result():
        try:
            result = await coro
            future.set_result(result)
        except Exception as e:
            future.set_exception(e)
    
    # 将任务提交到事件循环
    asyncio.run_coroutine_threadsafe(run_and_set_result(), _notification_loop)
    return future

class TicketService:
    """工单服务类"""
    
    @staticmethod
    def create_ticket(
        db: Session, 
        ticket_data: TicketCreate, 
        created_by: User
    ) -> Ticket:
        """创建新工单（异常捕获与日志）"""
        if not Permission.can_create_ticket(created_by.role):
            raise AuthorizationError("没有权限创建工单")
        ticket = Ticket(
            title=ticket_data.title,
            description=ticket_data.description,
            priority=ticket_data.priority,
            category=ticket_data.category,
            created_by_id=created_by.id,
            tags=ticket_data.tags or [],
            custom_fields=ticket_data.custom_fields or {}
        )
        try:
            db.add(ticket)
            db.commit()
            db.refresh(ticket)
            logger.info(f"用户 {created_by.username} 创建了工单 #{ticket.id}")
            
            # 记录工单创建指标
            monitoring_service.record_ticket_created(
                priority=ticket.priority.value,
                status=ticket.status.value
            )
        except Exception as e:
            db.rollback()
            logger.error(f"创建工单失败: {e}")
            raise DatabaseError(f"工单创建失败: {str(e)}", "create_ticket")
        
        try:
            # 异步通知 - 使用改进的方法
            async def send_notification():
                try:
                    await NotificationService.notify_ticket_created(ticket, created_by)
                except Exception as e:
                    logger.error(f"发送工单创建通知失败: {e}")
            
            # 在后台线程中运行异步通知
            _run_async_notification(send_notification())
        except Exception as e:
            logger.error(f"启动工单创建通知失败: {e}")
            
        if ticket_settings.TICKET_AUTO_ASSIGN_ENABLED:
            try:
                TicketService.auto_assign_ticket(db, ticket)
            except Exception as e:
                logger.error(f"自动分配工单失败: {e}")
        if ticket_settings.TICKET_AUTO_RESPONSE_ENABLED:
            try:
                TicketService.auto_reply_to_ticket(db, ticket)
            except Exception as e:
                logger.error(f"自动回复工单失败: {e}")
        return ticket
    
    @staticmethod
    def get_ticket(db: Session, ticket_id: uuid.UUID, user: User) -> Ticket:
        """获取单个工单"""
        ticket = db.query(Ticket).filter(Ticket.id == ticket_id).first()
        
        if not ticket:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail="工单不存在",
            )
        
        # 权限检查：用户只能查看自己的工单，除非有查看所有工单的权限
        if (ticket.created_by_id != user.id and 
            not Permission.can_view_all_tickets(user.role)):
            raise HTTPException(
                status_code=status.HTTP_403_FORBIDDEN,
                detail="没有权限查看此工单",
            )
        
        return ticket
    
    @staticmethod
    def get_tickets(
        db: Session, 
        user: User, 
        filters: Optional[TicketFilter] = None,
        skip: int = 0, 
        limit: int = 100
    ) -> List[Ticket]:
        """获取工单列表"""
        query = db.query(Ticket)
        
        # 权限过滤：普通用户只能看到自己的工单
        if not Permission.can_view_all_tickets(user.role):
            query = query.filter(Ticket.created_by_id == user.id)
        
        # 应用筛选条件
        if filters:
            if filters.status:
                query = query.filter(Ticket.status.in_(filters.status))
            if filters.priority:
                query = query.filter(Ticket.priority.in_(filters.priority))
            if filters.category:
                query = query.filter(Ticket.category.in_(filters.category))
            if filters.assigned_to:
                query = query.filter(Ticket.assigned_to_id == filters.assigned_to)
            if filters.created_by:
                query = query.filter(Ticket.created_by_id == filters.created_by)
            if filters.tags:
                for tag in filters.tags:
                    query = query.filter(Ticket.tags.contains([tag]))
            if filters.created_after:
                query = query.filter(Ticket.created_at >= filters.created_after)
            if filters.created_before:
                query = query.filter(Ticket.created_at <= filters.created_before)
            if filters.search:
                search_pattern = f"%{filters.search}%"
                query = query.filter(
                    or_(
                        Ticket.title.ilike(search_pattern),
                        Ticket.description.ilike(search_pattern)
                    )
                )
        
        # 排序和分页
        tickets = query.order_by(desc(Ticket.created_at)).offset(skip).limit(limit).all()
        return tickets
    
    @staticmethod
    def update_ticket(
        db: Session, 
        ticket_id: uuid.UUID, 
        update_data: TicketUpdate, 
        user: User
    ) -> Ticket:
        """更新工单（异常捕获与日志）"""
        ticket = TicketService.get_ticket(db, ticket_id, user)
        if (update_data.status or update_data.assigned_to_id) and \
           not Permission.can_assign_tickets(user.role):
            raise HTTPException(
                status_code=status.HTTP_403_FORBIDDEN,
                detail="没有权限更新工单状态或分配",
            )
        if update_data.title is not None:
            ticket.title = update_data.title
        if update_data.description is not None:
            ticket.description = update_data.description
        if update_data.status is not None:
            ticket.status = update_data.status
            if update_data.status == TicketStatus.RESOLVED and not ticket.resolved_at:
                ticket.resolved_at = datetime.utcnow()
            elif update_data.status == TicketStatus.CLOSED and not ticket.closed_at:
                ticket.closed_at = datetime.utcnow()
        if update_data.priority is not None:
            ticket.priority = update_data.priority
        if update_data.category is not None:
            ticket.category = update_data.category
        if update_data.assigned_to_id is not None:
            ticket.assigned_to_id = update_data.assigned_to_id
        if update_data.tags is not None:
            ticket.tags = update_data.tags
        if update_data.custom_fields is not None:
            ticket.custom_fields = update_data.custom_fields
        ticket.updated_at = datetime.utcnow()
        try:
            db.commit()
            db.refresh(ticket)
            logger.info(f"用户 {user.username} 更新了工单 #{ticket.id}")
        except Exception as e:
            db.rollback()
            logger.error(f"更新工单失败: {e}")
            raise HTTPException(status_code=500, detail="工单更新失败")
        if update_data.status or update_data.assigned_to_id:
            try:
                # 异步通知 - 使用线程池执行器避免事件循环冲突
                import asyncio
                import concurrent.futures
                
                def run_async_notification():
                    try:
                        loop = asyncio.new_event_loop()
                        asyncio.set_event_loop(loop)
                        loop.run_until_complete(NotificationService.notify_ticket_updated(ticket, user))
                        loop.close()
                    except Exception as e:
                        logger.error(f"发送工单更新通知失败: {e}")
                
                # 在线程中执行异步通知
                with concurrent.futures.ThreadPoolExecutor(max_workers=1) as executor:
                    executor.submit(run_async_notification)
                    
            except Exception as e:
                logger.error(f"发送工单更新通知失败: {e}")
        return ticket
    
    @staticmethod
    def add_reply(
        db: Session, 
        ticket_id: uuid.UUID, 
        reply_data: TicketReplyCreate, 
        user: User
    ) -> TicketReply:
        """添加工单回复（异常捕获与日志）"""
        ticket = TicketService.get_ticket(db, ticket_id, user)
        reply = TicketReply(
            ticket_id=ticket.id,
            user_id=user.id,
            content=reply_data.content,
            is_internal=reply_data.is_internal,
            is_automated=reply_data.is_automated
        )
        try:
            db.add(reply)
            ticket.reply_count += 1
            ticket.updated_at = datetime.utcnow()
            if ticket.reply_count == 1 and not ticket.first_response_time:
                first_response_time = (datetime.utcnow() - ticket.created_at).total_seconds()
                ticket.first_response_time = int(first_response_time)
            db.commit()
            db.refresh(reply)
            logger.info(f"用户 {user.username} 回复了工单 #{ticket.id}")
        except Exception as e:
            db.rollback()
            logger.error(f"添加工单回复失败: {e}")
            raise HTTPException(status_code=500, detail="工单回复失败")
        try:
            # 异步通知 - 使用线程池执行器避免事件循环冲突
            import asyncio
            import concurrent.futures
            
            def run_async_notification():
                try:
                    loop = asyncio.new_event_loop()
                    asyncio.set_event_loop(loop)
                    loop.run_until_complete(NotificationService.notify_ticket_reply(ticket, reply, user))
                    loop.close()
                except Exception as e:
                    logger.error(f"发送工单回复通知失败: {e}")
            
            # 在线程中执行异步通知
            with concurrent.futures.ThreadPoolExecutor(max_workers=1) as executor:
                executor.submit(run_async_notification)
                
        except Exception as e:
            logger.error(f"发送工单回复通知失败: {e}")
        return reply
    
    @staticmethod
    def get_replies(
        db: Session, 
        ticket_id: uuid.UUID, 
        user: User,
        include_internal: bool = False
    ) -> List[TicketReply]:
        """获取工单回复列表"""
        ticket = TicketService.get_ticket(db, ticket_id, user)
        
        query = db.query(TicketReply).filter(TicketReply.ticket_id == ticket_id)
        
        # 普通用户不能查看内部备注
        if not include_internal and user.role == UserRole.USER:
            query = query.filter(TicketReply.is_internal == False)
        
        replies = query.order_by(TicketReply.created_at).all()
        return replies
    
    @staticmethod
    def auto_assign_ticket(db: Session, ticket: Ticket) -> bool:
        """自动分配工单（SQL排序选最空闲客服）"""
        # 直接用SQL聚合统计每个客服未完成工单数，按升序排序
        subq = (
            db.query(
                User.id.label("agent_id"),
                func.count(Ticket.id).label("ticket_count")
            )
            .outerjoin(Ticket, (User.id == Ticket.assigned_to_id) & (Ticket.status.in_([TicketStatus.OPEN, TicketStatus.IN_PROGRESS])))
            .filter(User.role == UserRole.AGENT, User.status == "active")
            .group_by(User.id)
            .order_by("ticket_count")
        )
        best_agent_row = subq.first()
        if not best_agent_row:
            return False
        best_agent_id = best_agent_row.agent_id
        best_agent = db.query(User).filter(User.id == best_agent_id).first()
        ticket.assigned_to_id = best_agent_id
        ticket.updated_at = datetime.utcnow()
        db.commit()
        db.refresh(ticket)
        logger.info(f"工单 #{str(ticket.id)[:8]} 自动分配给了客服 {best_agent.username}")
        try:
            # 异步通知需要等待
            async def send_assignment_notification():
                try:
                    await NotificationService.notify_ticket_assigned(ticket, best_agent)
                except Exception as e:
                    logger.error(f"发送工单分配通知失败: {e}")
            
            # 在后台线程中运行异步通知
            _run_async_notification(send_assignment_notification())
        except Exception as e:
            logger.error(f"启动工单分配通知失败: {e}")
        return True
    
    @staticmethod
    def auto_reply_to_ticket(db: Session, ticket: Ticket) -> bool:
        """自动回复工单"""
        # 这里可以集成多智能体工作流生成自动回复
        # 目前实现简单的欢迎回复
        
        welcome_message = f"""
        您好！感谢您提交工单。

        我们已经收到您的请求，工单编号为：#{str(ticket.id)[:8]}
        标题：{ticket.title}
        优先级：{ticket.priority.value}
        分类：{ticket.category.value}

        我们的客服团队会尽快处理您的请求。如果您有更多信息需要补充，请直接回复此工单。

        谢谢！
        """
        
        # 获取系统用户作为自动回复的发送者
        system_user = db.query(User).filter(User.username == "system").first()
        if not system_user:
            # 如果没有system用户，则创建一个
            # 为系统用户生成一个随机密码哈希
            import hashlib
            import secrets
            random_password = secrets.token_urlsafe(32)
            password_hash = hashlib.sha256(random_password.encode()).hexdigest()
            
            system_user = User(
                id=uuid.uuid4(),  # 显式指定ID
                username="system",
                email="system@example.com",
                password_hash=password_hash,  # 添加必需的password_hash字段
                display_name="系统自动回复",
                role=UserRole.ADMIN,
                status=UserStatus.ACTIVE
            )
            db.add(system_user)
            db.flush()  # 获取system_user的ID而不提交事务
        
        # 创建自动回复
        auto_reply = TicketReply(
            ticket_id=ticket.id,
            user_id=system_user.id,  # 使用系统用户ID而不是None
            content=welcome_message.strip(),
            is_internal=False,
            is_automated=True
        )
        
        db.add(auto_reply)
        
        # 更新工单统计
        ticket.reply_count += 1
        ticket.updated_at = datetime.utcnow()
        
        try:
            db.commit()
            logger.info(f"工单 #{str(ticket.id)[:8]} 收到自动回复")
        except Exception as e:
            db.rollback()
            logger.error(f"自动回复工单提交失败: {e}")
            return False
        
        return True
    
    @staticmethod
    def get_stats(db: Session, user: User) -> TicketStats:
        """获取工单统计信息（批量聚合查询）"""
        if not Permission.can_view_stats(user.role):
            raise HTTPException(
                status_code=status.HTTP_403_FORBIDDEN,
                detail="没有权限查看统计信息",
            )
        # 一次性聚合统计
        status_counts = dict(db.query(Ticket.status, func.count(Ticket.id)).group_by(Ticket.status).all())
        category_counts = dict(db.query(Ticket.category, func.count(Ticket.id)).group_by(Ticket.category).all())
        priority_counts = dict(db.query(Ticket.priority, func.count(Ticket.id)).group_by(Ticket.priority).all())
        total_tickets = db.query(func.count(Ticket.id)).scalar()
        open_tickets = status_counts.get(TicketStatus.OPEN, 0)
        in_progress_tickets = status_counts.get(TicketStatus.IN_PROGRESS, 0)
        resolved_tickets = status_counts.get(TicketStatus.RESOLVED, 0)
        closed_tickets = status_counts.get(TicketStatus.CLOSED, 0)
        avg_response_time = db.query(func.avg(Ticket.first_response_time)).filter(Ticket.first_response_time.isnot(None)).scalar()
        avg_resolution_time = db.query(func.avg(Ticket.resolution_time)).filter(Ticket.resolution_time.isnot(None)).scalar()
# 枚举补齐
        by_category = {cat: category_counts.get(cat, 0) for cat in TicketCategory}
        by_priority = {pri: priority_counts.get(pri, 0) for pri in TicketPriority}
        by_status = {st: status_counts.get(st, 0) for st in TicketStatus}
        return TicketStats(
            total_tickets=total_tickets,
            open_tickets=open_tickets,
            in_progress_tickets=in_progress_tickets,
            resolved_tickets=resolved_tickets,
            closed_tickets=closed_tickets,
            average_response_time=avg_response_time,
            average_resolution_time=avg_resolution_time,
            by_category=by_category,
            by_priority=by_priority,
            by_status=by_status
        )


# 全局票务服务实例
ticket_service = TicketService()


def create_ticket(title: str, description: str, priority: str = "medium") -> dict:
    """创建工单 - 用于服务网关的简化版本"""
    from src.config.database import get_db
    from src.models.ticket import TicketCreate
    from src.models.user import User
    
    try:
        db = next(get_db())
        
        # 确保标题长度符合要求
        if len(title) < 5:
            title = f"工单: {title}"  # 添加前缀使标题长度达标
        
        # 确保优先级是有效值
        valid_priorities = ["low", "medium", "high", "urgent"]
        if priority not in valid_priorities:
            priority = "medium"  # 默认优先级
        
        # 创建一个简单的工单数据对象
        ticket_data = TicketCreate(
            title=title,
            description=description,
            priority=priority,
            category="general"  # 默认分类
        )
        
        # 获取一个系统用户或默认用户（这里简化处理）
        system_user = db.query(User).filter(User.username == "admin").first()
        if not system_user:
            # 如果没有admin用户，使用第一个可用用户或创建默认用户
            system_user = db.query(User).first()
            if not system_user:
                # 如果没有任何用户，返回错误
                return {
                    "success": False,
                    "error": "系统中没有可用用户，无法创建工单",
                    "title": title
                }
        
        # 创建工单
        ticket = TicketService.create_ticket(db, ticket_data, system_user)
        
        return {
            "success": True,
            "ticket_id": str(ticket.id),
            "title": ticket.title,
            "status": ticket.status.value,
            "priority": ticket.priority.value,
            "created_at": ticket.created_at.isoformat(),
            "message": "工单创建成功"
        }
            
    except Exception as e:
        return {
            "success": False,
            "error": f"创建工单失败: {str(e)}",
            "title": title
        }


# 导出
__all__ = [
    "TicketService", 
    "ticket_service",
    "create_ticket"
]
